﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using WJP.ComponentModel;
using WJP.Dependency;
using WJP.Environment;
using WJP.Reflection;

namespace WJP
{
    /// <summary>
    /// 实现以下功能：
    /// * DomainController 的创建。
    /// * DomainController 的覆盖。
    /// * 管理控制器之间的依赖。（在创建 DomainController 时，为其建立监听程序。）
    /// </summary>
    public abstract class DomainControllerFactory
    {
        private static object synclock = new object();
        private static Dictionary<Type, DomainController> _controllers = new Dictionary<Type, DomainController>();
        private static bool _intialized;

        /// <summary>
        /// key: parent,
        /// value: child
        /// </summary>
        private static Dictionary<Type, Type> _overriedList = new Dictionary<Type, Type>();

        /// <summary>
        /// key: depended
        /// value: dependee list
        /// </summary>
        private static Dictionary<Type, List<Type>> _dependency = new Dictionary<Type, List<Type>>();

        /// <summary>
        /// 控制器创建成功的事件。
        /// </summary>
        public static event EventHandler<ControllerCreatedEventArgs> ControllerCreated;

        /// <summary>
        /// 创建指定类型的控制器。
        /// </summary>
        /// <typeparam name="TController"></typeparam>
        /// <returns></returns>
        public static TController Create<TController>() where TController : DomainController
        {
            return Create(typeof(TController)) as TController;
        }

        /// <summary>
        /// 创建指定类型的控制器
        /// </summary>
        /// <returns></returns>
        public static DomainController Create(Type controllerType)
        {
            if (_controllers.TryGetValue(controllerType, out DomainController domainController))
                return domainController;
            lock (synclock)
            {
                if (_controllers.TryGetValue(controllerType, out domainController))
                    return domainController;
                InitializeIf();
                Type type = GetOverride(controllerType);
                CheckVirtualMethods(type);
                if (PlatformEnvironment.NeetProxy())
                    domainController = IocManager.Instance.Resolve(controllerType) as DomainController;
                else
                    domainController = Activator.CreateInstance(type) as DomainController;

                _controllers.Add(controllerType, domainController);

                CreateDependee(domainController, type);

                ControllerCreated?.Invoke(null, new ControllerCreatedEventArgs(domainController));
            }

            return domainController;
        }

        /// <summary>
        /// 检查是否为虚方法
        /// </summary>
        /// <param name="type"></param>
        private static void CheckVirtualMethods(Type type)
        {
            foreach (MethodInfo method in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                if ((method.IsPublic || method.IsFamily) 
                    && !method.IsVirtual 
                    && (!Attribute.IsDefined(method.DeclaringType, typeof(IgnoreProxyAttribute)) && !Attribute.IsDefined(method, typeof(IgnoreProxyAttribute))) 
                    && (!(method.DeclaringType == typeof(DomainController)) && !(method.DeclaringType == typeof(object))))
                        throw new ControllerProxyException("控制器{0}方法{1}必须是virtual".FormatArgs(type.FullName, method.Name));
            }
        }

        /// <summary>
        /// 初始化构造函数
        /// </summary>
        private static void InitializeIf()
        {
            if (_intialized)
                return;
            var pluginManager = IocManager.Instance.Resolve<IPluginManager>();
            var types = pluginManager.GetPluginInfos().SelectMany(p => p.Assembly.GetTypes());
            foreach (Type type in types.Where(p => p.IsSubclassOf(typeof(DomainController))))
                RuntimeHelpers.RunClassConstructor(type.TypeHandle);
            _intialized = true;
        }

        /// <summary>
        /// 获取子控制器类型
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        private static Type GetOverride(Type parent)
        {
            Type type = null;
            while (_overriedList.TryGetValue(parent, out type))
                parent = type;
            return parent;
        }

        /// <summary>
        /// 使用子控制器来覆盖父控制器。
        /// </summary>
        /// <typeparam name="TParent"></typeparam>
        /// <typeparam name="TChild"></typeparam>
        public static void Override<TParent, TChild>() where TParent : DomainController where TChild : TParent
        {
            ErrorIfIntialized();
            _overriedList[typeof(TParent)] = typeof(TChild);
        }

        /// <summary>
        /// 是否已初始化
        /// </summary>
        private static void ErrorIfIntialized()
        {
            if (_intialized)
                throw new InvalidProgramException();
        }

        /// <summary>
        /// 创建依赖关系
        /// </summary>
        /// <param name="dependee"></param>
        /// <param name="depended"></param>
        internal static void CreateDependency(Type dependee, Type depended)
        {
            ErrorIfIntialized();
            List<Type> typeList = null;
            lock (_dependency)
            {
                if (!_dependency.TryGetValue(depended, out typeList))
                {
                    typeList = new List<Type>();
                    _dependency.Add(depended, typeList);
                }
                typeList.Add(dependee);
            }
        }

        /// <summary>
        /// 创建控制器监听关系
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="controllerType"></param>
        private static void CreateDependee(DomainController instance, Type controllerType)
        {
            foreach (Type key in TypeExtension.GetHierarchy(controllerType, typeof(DomainController)))
            {
                if (_dependency.TryGetValue(key, out List<Type> typeList))
                {
                    foreach (Type type in typeList)
                        Create(type).InnerDependon(instance);
                }
            }
        }
    }
}
